package idiolect.core;

import idiolect.helpers.*;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;

public class TopicEditorDialog extends JDialog implements ActionListener, ResponseAcceptor, ListSelectionListener, DocumentListener {
    private final JTextField topicNameField;
    private final JButton addButton;
    private final JButton removeButton;
    private final JButton sortButton;
    private final JButton loopButton;
    private final JTextField originalField;
    private final JButton transcriptionButton;
    private final JTextField transcriptionField;
    private final JTextArea translationArea;
    private final JList wordsList;
    private SymbolsPanel symbolsPanel;
    private final Topic topic;
    private TitledBorder wordsBorder;
    private Timer timer;
    private ImageIcon sortIcon;
    private ImageIcon unsortIcon;
    private DefaultListModel model;
    private boolean dirty = false;
    private ImageIcon startIcon;
    private ImageIcon stopIcon;

    public TopicEditorDialog(JFrame owner, final Topic topic) {
        super(owner, topic.name, false);
        this.topic = topic;
        JPanel total = new JPanel(new GridBagLayout());
        total.setOpaque(true);
        total.setBackground(UIManager.getColor("Textfield.background"));
        setContentPane(total);
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.gridy = 0;
        gbc.gridx = 0;
        gbc.gridwidth = 1;
        total.add(topicNameField = new JTextField(topic.name), gbc);

        gbc.gridy = 1;
        gbc.gridx = 0;
        gbc.gridwidth = 1;
        gbc.gridheight = 3;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weighty = 1;
        model = new DefaultListModel();
        for (Word word : topic.words)
            model.addElement(word);
        JScrollPane wordsPane = new JScrollPane(wordsList = new JList(model));
        total.add(wordsPane, gbc);

        gbc.gridy = 4;
        gbc.gridx = 0;
        gbc.gridwidth = 1;
        gbc.gridheight = 1;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weighty = 0;
        sortIcon = new ImageIcon(TopicEditorDialog.class.getResource("./sort.png"));
        unsortIcon = new ImageIcon(TopicEditorDialog.class.getResource("./unsort.png"));
        JPanel buttonsPanel = new JPanel(new GridLayout(1, 0));
        buttonsPanel.add(addButton = new JButton(new ImageIcon(TopicEditorDialog.class.getResource("./add.png"))));
        buttonsPanel.add(removeButton = new JButton(new ImageIcon(TopicEditorDialog.class.getResource("./remove.png"))));
        buttonsPanel.add(sortButton = new JButton(sortIcon));
        startIcon = new ImageIcon(TopicEditorDialog.class.getResource("./transtart.png"));
        stopIcon = new ImageIcon(TopicEditorDialog.class.getResource("./transtop.png"));
        buttonsPanel.add(loopButton = new JButton(startIcon));
        total.add(buttonsPanel, gbc);

        gbc.gridx = 3;
        gbc.gridy = 0;
        gbc.gridwidth = 1;
        gbc.weightx = 1;
        total.add(originalField = new JTextField(), gbc);

        gbc.gridx = 4;
        gbc.gridy = 0;
        gbc.gridwidth = 1;
        gbc.weightx = 0;
        gbc.fill = GridBagConstraints.NONE;
        total.add(transcriptionButton = new JButton("" + '\u00E6'), gbc);
        transcriptionButton.setFont(transcriptionButton.getFont().deriveFont(25F));


        gbc.gridx = 3;
        gbc.gridy = 1;
        gbc.gridwidth = 2;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        total.add(transcriptionField = new JTextField(), gbc);

        gbc.gridy = 2;
        gbc.gridheight = 3;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weightx = gbc.weighty = 1;
        JScrollPane translationPane = new JScrollPane(translationArea = new JTextArea(4, 30), JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        total.add(translationPane, gbc);

        gbc.gridx = 5;
        gbc.gridy = 0;
        gbc.gridheight = 5;
        gbc.weightx = gbc.weighty = 0;
        gbc.fill = GridBagConstraints.NONE;
        gbc.anchor = GridBagConstraints.NORTH;
        total.add(symbolsPanel = new SymbolsPanel(this), gbc);

        wordsPane.setBorder(wordsBorder = BorderFactory.createTitledBorder("Words"));
        updateCounter();
        originalField.setBorder(BorderFactory.createTitledBorder("Original"));
        transcriptionButton.setMargin(new Insets(0, 0, 0, 0));
        transcriptionField.setBorder(BorderFactory.createTitledBorder("Transcription"));
        translationArea.setLineWrap(true);
        translationArea.setWrapStyleWord(true);
        translationPane.setBorder(BorderFactory.createTitledBorder("Translation"));
        topicNameField.setBorder(BorderFactory.createTitledBorder("Topic name"));
        symbolsPanel.setBorder(BorderFactory.createTitledBorder("Transcription Pad"));
        addButton.setBorder(BorderFactory.createCompoundBorder(new EmptyBorder(2, 2, 2, 2), addButton.getBorder()));
        removeButton.setBorder(BorderFactory.createCompoundBorder(new EmptyBorder(2, 2, 2, 2), removeButton.getBorder()));
        sortButton.setBorder(BorderFactory.createCompoundBorder(new EmptyBorder(2, 2, 2, 2), sortButton.getBorder()));
        loopButton.setBorder(BorderFactory.createCompoundBorder(new EmptyBorder(2, 2, 2, 2), loopButton.getBorder()));

        addButton.setToolTipText("Add new words to dictionary");
        removeButton.setToolTipText("Remove selected word");
        sortButton.setToolTipText("Sort list");
        loopButton.setToolTipText("Retrieve all transcriptions");
        transcriptionButton.setToolTipText("Retrieve transcription for selected word");
        setLiveButton(false);

        originalField.addActionListener(this);
        transcriptionButton.addActionListener(this);
        addButton.addActionListener(this);
        removeButton.addActionListener(this);
        sortButton.addActionListener(this);
        loopButton.addActionListener(this);
        wordsList.addListSelectionListener(this);
        originalField.getDocument().addDocumentListener(this);
        transcriptionField.getDocument().addDocumentListener(this);
        translationArea.getDocument().addDocumentListener(this);
        topicNameField.getDocument().addDocumentListener(this);
        wordsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        if (wordsList.getModel().getSize() > 0)
            wordsList.setSelectedIndex(0);
        wordsList.setVisibleRowCount(7);
        wordsList.setCellRenderer(new DefaultListCellRenderer() {

            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                JLabel c = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (value instanceof Word && !((Word) value).isComplete()) {
                    c.setForeground(mix(c.getForeground(), c.getBackground(), .25F));
                    setText(getText() + " ~");
                }
                return c;
            }
        });

        timer = new Timer(15000, this);
        timer.setRepeats(true);
        addWindowListener(new WindowAdapter() {
            public void windowOpened(WindowEvent e) {
                timer.start();
                wordsList.requestFocus();
            }

            public void windowClosing(WindowEvent e) {
                if (dirty) {
                    topic.save();
                    dirty = false;
                }
                timer.stop();
            }
        });
        addButton.registerKeyboardAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
        addButton.registerKeyboardAction(this, KeyStroke.getKeyStroke('='), JComponent.WHEN_IN_FOCUSED_WINDOW);
        removeButton.registerKeyboardAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
        removeButton.registerKeyboardAction(this, KeyStroke.getKeyStroke('_'), JComponent.WHEN_IN_FOCUSED_WINDOW);

        pack();
        setMinimumSize(getPreferredSize());
        setLocationRelativeTo(owner);
    }

    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == timer) {
            if (dirty) {
                topic.save();
                dirty = false;
            }
            return;
        }
        if (e.getSource() == originalField || e.getSource() == transcriptionButton) {
            setLiveButton(true);
            new CompositeTranscriptionHelper(originalField.getText(), this, wordsList.getSelectedIndex()).doRequest();
            return;
        }
        if (e.getSource() == addButton) {
            String s = JOptionPane.showInputDialog(TopicEditorDialog.this, "Enter new words");
            if (s != null && s.length() > 0) {
                s = s.trim();
                if (s.length() == 0)
                    return;
                String[] words = s.split("[\\t\\n,;]");
                if (words.length == 1)
                    words = s.split("\\s{2}");
                if (words.length > 1) {
                    for (String word : words) {
                        word = word.trim();
                        if (word.length() == 0)
                            continue;
                        Word w = new Word(topic.getMaxIndex() + 1, word.toLowerCase(), "", "");
                        if (!topic.words.contains(w))
                            topic.words.add(w);
                        DefaultListModel m = (DefaultListModel) wordsList.getModel();
                        m.addElement(w);
                    }
                } else {
                    final Word w = new Word(topic.getMaxIndex() + 1, s.toLowerCase(), "", "");
                    if (topic.words.contains(w)) {
                        JOptionPane.showMessageDialog(this, "This word is already added", "Warning", JOptionPane.WARNING_MESSAGE);
                        for (Word word : topic.words) {
                            if (word.original.equals(s)) {
                                wordsList.setSelectedValue(word, true);
                                break;
                            }
                        }
                        return;
                    }
                    topic.words.add(w);
                    DefaultListModel m = (DefaultListModel) wordsList.getModel();
                    m.addElement(w);
                    wordsList.setSelectedValue(w, true);
                    actionPerformed(new ActionEvent(originalField, 0, ""));
                    dirty = true;
                    updateCounter();
                }
            }
            return;
        }

        if (e.getSource() == removeButton) {
            final int index = wordsList.getSelectedIndex();
            if (index == -1)
                return;

            if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(TopicEditorDialog.this, "Are you sure you want to remove word '" + topic.words.get(index) + "'?", "Confirmation", JOptionPane.YES_NO_OPTION)) {
                topic.words.remove(index);
                DefaultListModel m = (DefaultListModel) wordsList.getModel();
                m.removeElementAt(index);
                dirty = true;
                updateCounter();
            }
            return;
        }

        if (e.getSource() == sortButton) {
            final Object o = wordsList.getSelectedValue();
            topic.setSortedMode(!topic.isSorted());
            sortButton.setIcon(topic.isSorted() ? unsortIcon : sortIcon);
            model.clear();
            for (Word word : topic.words)
                model.addElement(word);
            wordsList.setSelectedValue(o, true);
            return;
        }
        if (e.getSource() == loopButton) {
            boolean start = loopButton.getIcon() == startIcon;
            loopButton.setIcon(start ? stopIcon : startIcon);
            if (!start)
                return;

            int i = getEmptyTranscriptionIndex(wordsList.getSelectedIndex());
            if (i == -1) {
                loopButton.setIcon(startIcon);
                return;
            }
            wordsList.setSelectedValue(wordsList.getModel().getElementAt(i), true);
            final int i1 = i;
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    new CompositeTranscriptionHelper(originalField.getText(), TopicEditorDialog.this, -1 - i1).doRequest();
                }
            });
            return;
        }
        //symbols pad
        transcriptionField.requestFocus();
        final int pos = transcriptionField.getCaretPosition();
        transcriptionField.setText(
                new StringBuilder(transcriptionField.getText()).
                        insert(pos, ((JButton) e.getSource()).getActionCommand().charAt(0)).toString());
        transcriptionField.setCaretPosition(pos + 1);
    }

    private int getEmptyTranscriptionIndex(int startIndex) {
        int i;
        int size = wordsList.getModel().getSize();
        if (size == 0)
            return -1;
        for (i = startIndex; i < size; i++) {
            Word word = (Word) wordsList.getModel().getElementAt(i);
            if (word.transcription.length() == 0)
                return i;
        }
        return -1;
    }

    private void updateCounter() {
        StringBuilder sb = new StringBuilder("Words");
        if (wordsList.getModel().getSize() > 0)
            sb.append(" (").append(wordsList.getModel().getSize()).append(")");
        wordsBorder.setTitle(sb.toString());
    }


    public void setResponse(AbstractHelper source, String response) {
        if (!source.getRequest().equals(originalField.getText())) {
            setLiveButton(false);
            if (loopButton.getIcon() == stopIcon)
                loopButton.setIcon(startIcon);
            return;
        }
        if (source.getTaskID() == HTMLDialog.ADD_TASK_ID || source.getTaskID() == HTMLDialog.REPLACE_TASK_ID) {
            translationArea.setText(
                    (source.getTaskID() == HTMLDialog.ADD_TASK_ID ? translationArea.getText() : "")
                            + source.getResponse());
            return;
        }
        if (source instanceof CompositeTranscriptionHelper) {
            final int taskID = source.getTaskID();
            if (taskID < 0 && loopButton.getIcon() == startIcon)
                return;
            if (response != null)
                transcriptionField.setText(response);
            if (taskID >= 0)
                new MultitranTranslationHelper(source.getRequest(), this).doRequest();
            else {
                Timer t200 = new Timer(50, new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        int i = getEmptyTranscriptionIndex(-taskID);
                        if (i == -1) {
                            loopButton.setIcon(startIcon);
                            return;
                        }
                        wordsList.setSelectedValue(wordsList.getModel().getElementAt(i), true);
                        final int i1 = i;
                        SwingUtilities.invokeLater(new Runnable() {
                            public void run() {
                                new CompositeTranscriptionHelper(originalField.getText(), TopicEditorDialog.this, -1 - i1).doRequest();
                            }
                        });
                    }
                });
                t200.setRepeats(false);
                t200.start();
            }
            return;
        }
        if (source instanceof MultitranTranslationHelper) {
            if (response != null)
                try {
                    new HTMLDialog(this, source.getRequest(), response).setVisible(true);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            setLiveButton(false);
            return;
        }
    }

    private void setLiveButton(boolean live) {
        transcriptionButton.setEnabled(!live);
    }

    public void valueChanged(ListSelectionEvent e) {
        if (e.getValueIsAdjusting())
            return;
        if (e.getSource() == wordsList) {
            final int index = wordsList.getSelectedIndex();
            if (index != -1) {
                Word word = (Word) wordsList.getModel().getElementAt(index);
                originalField.setText(word.original);
                transcriptionField.setText(word.transcription);
                translationArea.setText(word.translation);
                translationArea.setCaretPosition(0);
            } else {
                originalField.setText("");
                transcriptionField.setText("");
                translationArea.setText("");
            }
            originalField.setEnabled(index != -1);
            transcriptionField.setEnabled(index != -1);
            translationArea.setEnabled(index != -1);
            symbolsPanel.setEnabled(index != -1);
            removeButton.setEnabled(index != -1);
            return;
        }
    }

    public void insertUpdate(DocumentEvent e) {
        documentChanged(e);
    }

    public void removeUpdate(DocumentEvent e) {
        documentChanged(e);
    }

    public void changedUpdate(DocumentEvent e) {
        documentChanged(e);
    }

    private void documentChanged(DocumentEvent e) {
        final int index = wordsList.getSelectedIndex();
        if (index == -1)
            return;
        dirty = true;
        final Word word = (Word) wordsList.getModel().getElementAt(index);
        if (e.getDocument() == originalField.getDocument()) {
            word.original = originalField.getText();
        }
        if (e.getDocument() == transcriptionField.getDocument()) {
            int pos = transcriptionField.getCaretPosition();
            final StringBuilder tValue = new StringBuilder(transcriptionField.getText().replace('\'', '\u02C8'));
            boolean changed = false;
            for (int i = 0; i < tValue.length(); i++) {
                if (Constants.TOTAL.indexOf(tValue.charAt(i)) == -1 && tValue.charAt(i) != ' ') {//space is allowed anyway for phrases
                    tValue.deleteCharAt(i);
                    if (i < pos)
                        pos--;
                    changed = true;
                }
            }
            if (changed) {
                Toolkit.getDefaultToolkit().beep();
                final int pos1 = pos;
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        transcriptionField.setText(tValue.toString());
                        transcriptionField.setCaretPosition(Math.max(0, pos1));
                        word.transcription = tValue.toString();
                    }
                });
            } else {
                word.transcription = tValue.toString();
            }
        }
        if (e.getDocument() == translationArea.getDocument()) {
            word.translation = translationArea.getText();
        }
        if (e.getDocument() == topicNameField.getDocument()) {
            topic.name = topicNameField.getText();
            setTitle(topicNameField.getText());
        }
        wordsList.revalidate();
        wordsList.repaint();
    }

    private static Color mix(Color c1, Color c2, float w) {
        w = Math.min(1, Math.max(0, w));
        float[] rgb1 = c1.getRGBColorComponents(null);
        float[] rgb2 = c2.getRGBColorComponents(null);
        return new Color(
                Math.min(1, Math.max(0, rgb1[0] + w * (rgb2[0] - rgb1[0]))),
                Math.min(1, Math.max(0, rgb1[1] + w * (rgb2[0] - rgb1[1]))),
                Math.min(1, Math.max(0, rgb1[2] + w * (rgb2[0] - rgb1[2])))
        );
    }
}
